﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using RatingAuthor;

namespace ConditionTreeTestStand.Nodes
{
  public class NodeFactory
  {
    public NodeFactory(NodeContext nodeContext)
    {
      _nodeContext = nodeContext;
    }

    private readonly NodeContext _nodeContext;
    public Func<Book, bool> Compile(params NodeViewModel[] children)
    {
      return MakeTree(children).Compile();
    }

    public RootNodeViewModel MakeTree(params NodeViewModel[] children)
    {
      var node = new RootNodeViewModel(_nodeContext);
      foreach (var child in children)
      {
        child.Parent = node;
        node.Children.Add(child);
      }
      return node;
    }
    public GroupNodeViewModel Node(string o, params NodeViewModel[] children)
    {
      var node = new GroupNodeViewModel(null, _nodeContext);
      node.Operator = node.OperatorPossibleValues
        .Select(op => op.Operator)
        .First(op => op.Name == o);
      foreach (var child in children)
      {
        child.Parent = node;
        node.Children.Add(child);
      }
      return node;
    }
    public ConditionNodeViewModel Node<T>(string first, string o, T second)
    {
      var node = new ConditionNodeViewModel(null, _nodeContext)
                   {
                     FirstOperand = Column.BookColumns.First(c => c.Name == first),
                   };
      node.Operator = node.OperatorPossibleValues
        .Select(op => op.Operator)
        .First(op => op.Symbol == o || op.Name == o);
      var sizeSecondOp = (SecondOperandViewModel<T>)node.OtherOperands;
      sizeSecondOp.SecondOperand = second;
      return node;
    }
    public ConditionNodeViewModel NodeStr(string first, string o, string second)
    {
      var node = new ConditionNodeViewModel(null, _nodeContext)
                   {
                     FirstOperand = Column.BookColumns.First(c => c.Name == first),
                   };
      node.Operator = node.OperatorPossibleValues
        .Select(op => op.Operator)
        .First(op => op.Symbol == o || op.Name == o);
      var sizeSecondOp = (ISingleSecondOperand)node.OtherOperands;
      sizeSecondOp.StrValue = second;
      return node;
    }
    public GenresSet Genres(params string[] g)
    {
      return _nodeContext.GenresLookup.Lookup(g);
    }

  }

  public class XmlSerializer
  {
    public static RootNodeViewModel Deserialize(string data, NodeContext ctx)
    {
      return Deserialize(XDocument.Parse(data).Root, ctx);
    }

    private static RootNodeViewModel Deserialize(XElement src, NodeContext ctx)
    {
      var factory = new NodeFactory(ctx);
      return factory.MakeTree(CreateNode(factory, src));
    }

    private static NodeViewModel CreateNode(NodeFactory factory, XElement element)
    {
      var first = element.Attribute("FirstOperand");
      var op = element.MandatoryAttr("Operator");
      if (first != null)
      {
        var second = element.MandatoryAttr("SecondOperand");
        return factory.NodeStr(first.Value, op, second);
      }

      var children = element.Elements()
        .Select(e => CreateNode(factory, e))
        .ToArray();

      return factory.Node(op, children);
    }

    public static string Serialize(RootNodeViewModel tree)
    {
      var element = Serialize(tree.Children[0]);
      return element == null ? "" : element.ToString();
    }

    private static XElement Serialize(NodeViewModel node)
    {
      var group = node as GroupNodeViewModel;
      if (group != null)
      {
        if (group.Operator == null) return null;
        return new XElement("Node",
          new object[] { new XAttribute("Operator", group.Operator.Name) }
          .Concat(group.Children.Select(Serialize).Where(s => s != null)));
      }
      var condition = node as ConditionNodeViewModel;
      if (condition != null)
      {
        if (condition.FirstOperand == null) return null;
        if (condition.Operator == null) return null;
        var singleSecondOperand = ((ISingleSecondOperand)condition.OtherOperands);
        if (singleSecondOperand == null) return null;
        return new XElement("Node",
          new XAttribute("FirstOperand", condition.FirstOperand.Name),
          new XAttribute("Operator", condition.Operator.Name),
          new XAttribute("SecondOperand", singleSecondOperand.StrValue));
      }
      throw new NotSupportedException();
    }
  }
  public static class XNodeExt
  {
    public static string MandatoryAttr(this XElement element, string attrName)
    {
      var arrt = element.Attribute(attrName);
      if (arrt == null) throw new Exception(attrName + " attribute is mandatory");
      return arrt.Value;
    }
  }
}